package com.han.yrpc.core.runtime.socket;

import com.han.yrpc.core.provider.AbstractYrpcServer;
import com.han.yrpc.core.provider.ServiceCache;
import com.han.yrpc.core.runtime.YRequest;
import com.han.yrpc.core.runtime.YResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
@Component
public class SocketYrpcServer extends AbstractYrpcServer {

    private final int DEFAULT_PORT = 8888;
    private int port = DEFAULT_PORT;

    private final ExecutorService executor =
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    public SocketYrpcServer(ServiceCache serviceCache) {
        super(serviceCache);
    }

    @Override
    public int getPort() {
        return this.port;
    }

    @Override
    public void start() {
        try {
            String host = InetAddress.getLocalHost().getHostAddress();
            ServerSocket serverSocket = new ServerSocket();
            serverSocket.bind(new InetSocketAddress(port));
            log.info("server start ...  host:[{}],port:[{}]", host, port);
            while (true) {
                Socket socket = serverSocket.accept();
                executor.execute(() -> this.serverTask(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void serverTask(Socket socket) {

        try (ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
             ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());) {

            YRequest yrequest = (YRequest) inputStream.readObject();
            Object result = handlerRequest(yrequest);

            YResponse<Object> response = YResponse.builder().code(1).msg("").data(result).build();
            outputStream.writeObject(response);
            outputStream.flush();
        } catch (Exception e) { // todo 日志不合理，发生错误无法排查
            log.error("occur Exception: {}", e.getMessage());
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    private Object handlerRequest(YRequest yrequest) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Object object = this.getServiceImpl(yrequest.getServiceName());
        if (object == null) {
            throw new ClassNotFoundException(yrequest.getServiceName() + " not found");
        }

        Method method = object.getClass().getMethod(yrequest.getMethodName(), yrequest.getParameterTypes());
        return method.invoke(object, yrequest.getArgs());
    }

}
